#include <linux/usb.h>

#define VENDOR_ID 0x08F7
#define PRODUCT_ID 0x0002
static struct usb_device_id id_table[]={
	{USB_DEVICE(VENDOR_ID, PRODUCT_ID)}, {},
};
MODULE_DEVICE_TABLE(usb, id_table);

static struct usb_device *udev;
static int intbInterval;
static int intpipe, ctrlpipe;
struct usb_anchor submitted; /* in case we need to retract our submissions */
static struct urb *urb;
static char *buffer;
static const char initdev[8]={0x1a,0,0,0,0,0,0,0}, 
	ledgreen[8]={0x1d,0x80,0x10,0,0,0,0,0},
	ledyellow[8]={0x1d,0,4,0,0,0,0,0},
	ledoff[8]={0x1d,0x80,0,0,0,0,0,0},
	startmeasure[8]={0x18,0,0,0,0,0,0,0},
	stopmeasure[8]={0x19,0,0,0,0,0,0,0};

#define CM(data) usb_control_msg(udev,ctrlpipe,9,0x21,0x0200,0,data,8,0)

void temprobe_complete(struct urb *urb){
	unsigned char *data;
	int v;

	if(urb->status)	return;

	data = (unsigned char*)urb->transfer_buffer;
	v = data[2]*256+data[3];
	printk("<3>%d.%02d 'C\n", v/0x7F, (v*100/0x7F)%100);
	
	if (usb_submit_urb(urb, GFP_ATOMIC) < 0)
		printk("<3>Cannot submit urb.\n");
}

int workflow(void){
	char *buf = kmalloc(8, GFP_KERNEL);
	if(!buf)
		return -ENOMEM;
	memcpy(buf, initdev, 8);
	if(CM(buf)<0) return -1;
	memcpy(buf, ledgreen, 8);
	if(CM(buf)<0) return -1;
	memcpy(buf, startmeasure, 8);
	if(CM(buf)<0) return -1;
	memcpy(buf, "\x1b\3\xe8\0\0\0\0\0", 8);
	if(CM(buf)<0) return -1;
	kfree(buf);
	return 0;
}

static void detect_altsetting(struct usb_device *udev, struct usb_host_interface *as, int index){
	int l;
	struct usb_host_endpoint *ep;
	if(as == 0) return;
	for(l=0;l<as->desc.bNumEndpoints;l++)
	  {
		ep = &as->endpoint[l];
		if(ep == 0) continue;
		if( usb_endpoint_is_int_in(&ep->desc) )
		  {
			intbInterval=ep->desc.bInterval;
			intpipe=usb_rcvintpipe(udev, ep->desc.bEndpointAddress);						
		  }
	  }
}

static void detect_interface(struct usb_device *udev, struct usb_interface *ifp, int index){
	int k;
	if(ifp == 0) return;
	for(k=0;k<ifp->num_altsetting;k++)
		detect_altsetting(udev, &ifp->altsetting[k], k);
}

static void detect_configuration(struct usb_device *udev, struct usb_config_descriptor *cfg, int index){
	int j;
	if(cfg == 0) return;
	for(j=0;j<cfg->bNumInterfaces;j++)
		detect_interface(udev, udev->config[index].interface[j], j);
}

int temprobe_probe(struct usb_interface *interface, const struct usb_device_id *id){
	int i;

	udev = interface_to_usbdev(interface);
	usb_get_dev(udev);

	for(i=0;i<udev->descriptor.bNumConfigurations;i++)
		detect_configuration(udev, &udev->config[i].desc, i);
	if(!intpipe)
		return -EPIPE; /* Broken pipe */

	ctrlpipe=usb_sndctrlpipe(udev,0);
	init_usb_anchor(&submitted);

	urb = usb_alloc_urb(0, GFP_KERNEL);
	if(!urb)
		return -ENOMEM;
	buffer=usb_buffer_alloc(udev, 8, GFP_KERNEL, &urb->transfer_dma);
	if (!buffer)
		goto fail;

	if(workflow())
		return -1;
		
	usb_fill_int_urb(urb, udev, intpipe, buffer, 8, temprobe_complete, NULL, intbInterval);
	usb_anchor_urb(urb, &submitted);
	if (usb_submit_urb(urb, GFP_ATOMIC) < 0){
		printk("<3>Cannot submit urb!\n");
		goto fail;
	}

	return 0;

fail:
	usb_free_urb(urb);
	usb_put_dev(udev);
	return -1;
}
void temprobe_disconnect(struct usb_interface *interface){
	char *buf = kmalloc(8, GFP_KERNEL);
	if(buf)
	  {
		memcpy(buf, stopmeasure, 8); CM(buf);
		memcpy(buf, ledyellow, 8); CM(buf);
		kfree(buf);
	  }
	usb_buffer_free(urb->dev, urb->transfer_buffer_length,
		urb->transfer_buffer, urb->transfer_dma);
	usb_free_urb(urb);
	usb_kill_anchored_urbs(&submitted);
	usb_put_dev(udev);
}
struct usb_driver temprobe_driver={
	.name =	"9263_temprobe_driver",
	.probe = temprobe_probe,
	.disconnect = temprobe_disconnect,
	.id_table =	id_table,
};
int __init temprobe_init(void){
	return usb_register(&temprobe_driver);
}
void __exit temprobe_exit(void){
	usb_deregister(&temprobe_driver);
}
module_init(temprobe_init);
module_exit(temprobe_exit);
MODULE_LICENSE("GPL");
